package com.gcxy.config.listener;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.StringUtils;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.gcxy.config.handler.ErrorSheetWriteHandler;
import com.gcxy.constant.StringConst;
import com.gcxy.core.file.AddUserTemplate;
import com.gcxy.dto.ExcelImportErrDto;
import com.gcxy.vo.student.StudentTeacherVO;
import com.gcxy.vo.user.UserPureVO;
import org.apache.poi.ss.usermodel.HorizontalAlignment;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;


/**
 * @author Pep7Chiao
 */
public class UserImportListener extends AnalysisEventListener<AddUserTemplate> {

    /**
     * 标记文件是否有错
     */
    public boolean errorFlag = false;
    public List<UserPureVO> userVOList;
    private HttpServletResponse response;
    private Integer agencyId;
    private List<StudentTeacherVO> studentTeacherVOS;
    /**
     * excel对应的实体对象的反射类
     */
    private Class<AddUserTemplate> clazz = AddUserTemplate.class;
    /**
     * 错误数据结果集
     */
    private List<ExcelImportErrDto> errList = new ArrayList<>();

    /**
     * 正确信息数据集
     */
    public List<AddUserTemplate> successList = new ArrayList<>();

    public UserImportListener(HttpServletResponse response, List<UserPureVO> userVOList, List<StudentTeacherVO> studentTeacherVOS, Integer agencyId) {
        this.response = response;
        this.userVOList = userVOList;
        this.studentTeacherVOS = studentTeacherVOS;
        this.agencyId = agencyId;
    }


    @Override
    public void invoke(AddUserTemplate addUserTemplate, AnalysisContext analysisContext) {
        Map<Integer, String> resultMap = checkInformation(addUserTemplate);
        Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
        ExcelImportErrDto excelImportErrObjectDto = new ExcelImportErrDto(rowIndex, addUserTemplate, resultMap);
        errList.add(excelImportErrObjectDto);
    }

    /**
     * 验证表头
     *
     * @param headMap 表头
     * @param context 上下文
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        super.invokeHeadMap(headMap, context);
        // 获取最后一列和倒数第二列的表头
        if (clazz != null) {
            try {
                Map<Integer, String> indexNameMap = getIndexNameMap(clazz,headMap);
                Set<Integer> keySet = indexNameMap.keySet();
                if (keySet.size() != headMap.size()) {
                    throw new ExcelAnalysisException("解析excel出错，请传入正确格式的excel");
                }
                for (Integer key : keySet) {
                    if (StringUtils.isEmpty(headMap.get(key))) {
                        throw new ExcelAnalysisException("解析excel出错，请传入正确格式的excel");
                    }
                    if (!headMap.get(key).equals(indexNameMap.get(key))) {
                        throw new ExcelAnalysisException("解析excel出错，请传入正确格式的excel");
                    }
                }

            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 验证数据
     *
     * @param addUserTemplate 被验证的数据对象
     * @return 验证结果集
     */
    private Map<Integer, String> checkInformation(AddUserTemplate addUserTemplate) {
        Map<Integer, String> resultMap = new HashMap<>();
        //1、验证学号
        //1.1学号不能为空
        if (StringUtils.isBlank(addUserTemplate.getStudentAccount())) {
            resultMap.put(0, "学号不能为空！");
            errorFlag = true;
        } else if (addUserTemplate.getStudentAccount().length() < StringConst.STUDENT_ACCOUNT_LENGTH) {
            //1.2学号长度不能超过15位
            resultMap.put(0, "学号长度不能少于"+StringConst.STUDENT_ACCOUNT_LENGTH+"位！");
            errorFlag = true;
        } else if (!Pattern.matches("^[a-zA-Z0-9]+$", addUserTemplate.getStudentAccount())) {
            //1.3学号只能是英文或者数字
            resultMap.put(0, "学号只能是英文或者数字！");
            errorFlag = true;
        } else if (successList.stream().map(AddUserTemplate::getStudentAccount).collect(Collectors.toList()).contains(addUserTemplate.getStudentAccount())) {
            //1.4学号不能重复
            resultMap.put(0, "批量上传学生信息中学号重复！");
            errorFlag = true;
        }
        //姓名
        //2.验证姓名
        //2.1姓名不能为空
        if (StringUtils.isBlank(addUserTemplate.getStudentName())) {
            resultMap.put(1, "姓名不能为空!");
            errorFlag = true;
        } else if (addUserTemplate.getStudentName().length() > StringConst.USER_NAME_LENGTH) {
            //2.2姓名长度不能超过10位
            resultMap.put(1, "姓名长度不能超过"+StringConst.USER_NAME_LENGTH+"位!");
            errorFlag = true;
        } else if (!Pattern.matches("^[\\u4e00-\\u9fa50-9]+$", addUserTemplate.getStudentName())) {
            //2.3学生姓名只能是中文或者数字
            resultMap.put(1, "姓名只能是中文或者数字！");
            errorFlag = true;
        }
        //3.验证岗位
        //3.1岗位不能为空
        if (StringUtils.isBlank(addUserTemplate.getPostName())) {
            resultMap.put(2, "岗位不能为空!");
            errorFlag = true;
        } else if (addUserTemplate.getPostName().length() > 20) {
            //3.2岗位长度不能超过20位
            resultMap.put(2, "岗位长度不能超过20位!");
            errorFlag = true;
        }
        //4.验证班级
        //4.1班级不能为空
        if (StringUtils.isBlank(addUserTemplate.getClassInfo())) {
            resultMap.put(3, "班级不能为空!");
            errorFlag = true;
        } else if (addUserTemplate.getClassInfo().length() > 20) {
            //4.2班级长度不能超过20位
            resultMap.put(3, "班级长度不能超过20位!");
            errorFlag = true;
        }
        //5.验证项目
        //5.1项目不能为空
        if (StringUtils.isBlank(addUserTemplate.getProjectList())) {
            resultMap.put(4, "项目列表不能为空!");
            errorFlag = true;
        } else {
            String[] projectNames = addUserTemplate.getProjectList().split("@");
            for (String projectName : projectNames) {
                if (projectName.length() > 20) {
                    //4.2班级长度不能超过20位
                    resultMap.put(4, "项目名称长度不能超过20位!");
                    errorFlag = true;
                    break;
                }
            }
        }

        //6.验证教师账号
        //6.1教师账号不能为空
        if (StringUtils.isBlank(addUserTemplate.getNominalTeacherAccount())) {
            resultMap.put(5, "教师账号不能为空!");
            errorFlag = true;
        } else if (addUserTemplate.getNominalTeacherAccount().length() > StringConst.TEACHER_ACCOUNT_LENGTH) {
            //6.2教师账号长度不能超过15位
            resultMap.put(5, "教师账号长度不能超过"+StringConst.TEACHER_ACCOUNT_LENGTH+"位!");
            errorFlag = true;
        } else if (!Pattern.matches("^[a-zA-Z0-9]+$", addUserTemplate.getNominalTeacherAccount())) {
            //6.3教师账号只能是英文或者数字
            resultMap.put(5, "教师账号只能是英文或者数字！");
            errorFlag = true;
        }
        //数据库中如果包含该教师账号已存在,但不属于当前组织机构，且已绑定学生:提示信息:“当前老师不在本组织，且绑定了其他组织的学生，操作无法继续。”
        extracted(addUserTemplate.getNominalTeacherAccount(), resultMap , 5);

        //7.验证教师姓名
        //7.1教师姓名不能为空
        if (StringUtils.isBlank(addUserTemplate.getNominalTeacherName())) {
            resultMap.put(6, "教师姓名不能为空!");
            errorFlag = true;
        } else if (addUserTemplate.getNominalTeacherName().length() > StringConst.USER_NAME_LENGTH) {
            //7.2教师姓名长度不能超过10位
            resultMap.put(6, "教师姓名长度不能超过"+StringConst.USER_NAME_LENGTH+"位!");
            errorFlag = true;
        } else if (!Pattern.matches("^[\\u4e00-\\u9fa50-9]+$", addUserTemplate.getNominalTeacherName())) {
            //7.3教师姓名只能是中文或者数字
            resultMap.put(6, "教师姓名只能是中文或者数字！");
            errorFlag = true;
        }
        //8.验证实际指导教师账号
        //8.1实际指导教师账号长度不能超过15位
        if (!StringUtils.isBlank(addUserTemplate.getTeacherAccount()) && addUserTemplate.getTeacherAccount().length() > StringConst.TEACHER_ACCOUNT_LENGTH) {
            resultMap.put(7, "实际指导教师账号长度不能超过"+StringConst.TEACHER_ACCOUNT_LENGTH+"位!");
            errorFlag = true;
        } else if (!StringUtils.isBlank(addUserTemplate.getTeacherAccount()) && !Pattern.matches("^[a-zA-Z0-9]+$", addUserTemplate.getTeacherAccount())) {
            //8.2教师账号只能是英文或者数字
            resultMap.put(7, "教师账号只能是英文或者数字！");
            errorFlag = true;
        }
        extracted(addUserTemplate.getTeacherAccount(), resultMap , 7);
        //9.验证实际指导教师姓名
        //9.1实际指导教师姓名长度不能超过10位
        if (!StringUtils.isBlank(addUserTemplate.getTeacherName()) && addUserTemplate.getTeacherName().length() > StringConst.USER_NAME_LENGTH) {
            resultMap.put(8, "实际指导教师姓名长度不能超过"+StringConst.USER_NAME_LENGTH+"位!");
            errorFlag = true;
        } else if (!StringUtils.isBlank(addUserTemplate.getTeacherAccount()) && !Pattern.matches("^[\\u4e00-\\u9fa50-9]+$", addUserTemplate.getTeacherName())) {
            //9.2教师姓名只能是中文或者数字
            resultMap.put(8, "教师姓名只能是中文或者数字！");
            errorFlag = true;
        }
        //没有错则添加到新增集合中，错误则都不新增
        if (!errorFlag) {
            successList.add(addUserTemplate);
        }
        return resultMap;
    }

    private void extracted(String account, Map<Integer, String> resultMap,int num) {
        if (CollectionUtils.isNotEmpty(userVOList)) {
            //根据账号查询是否有用户
            List<UserPureVO> userVOS = userVOList.stream().filter(teacher -> teacher.getUsername().equals(account)).collect(Collectors.toList());
            UserPureVO userVO = new UserPureVO();
            List<StudentTeacherVO> studentTeacherVOList = null;
            if (CollectionUtils.isNotEmpty(userVOS)) {
                userVO =userVOS.get(0);
            }
            if (CollectionUtils.isNotEmpty(studentTeacherVOS)) {
                Integer userId = userVO.getId();
                studentTeacherVOList = studentTeacherVOS.stream()
                        .filter(studentTeacherVO -> studentTeacherVO.getHonoraryTeacherId() == userId
                                || studentTeacherVO.getPracticalTeacherId() == userId)
                        .collect(Collectors.toList());
            }
            boolean flag = ObjectUtils.isNotEmpty(userVO)
                    && ObjectUtils.isNotEmpty(userVO.getAgencyId())
                    && userVO.getAgencyId() != agencyId
                    && CollectionUtils.isNotEmpty(studentTeacherVOList);
            //判断账号已存在，但不属于本组织，且绑定有学生
            if (flag) {
                resultMap.put(num, "当前老师不在本组织，且绑定了其他组织的学生，操作无法继续！");
                errorFlag = true;
            }
        }
    }

    /**
     * 根据结果集导出错误信息
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (errorFlag) {
            //返回错误信息
            try {
                exportErrorExcel();
            } catch (IOException e) {
                e.printStackTrace();
                throw new RuntimeException("错误表格导出异常！");
            }
        }
    }

    /**
     * 导出错误信息Excel
     */
    private void exportErrorExcel() throws IOException {
        //错误结果集
        List<AddUserTemplate> errorDtoList = errList.stream().map(ExcelImportErrDto::getAddUserTemplate).collect(Collectors.toList());
        List<Map<Integer, String>> errMsgList = errList.stream().map(ExcelImportErrDto::getErrMsgMap).collect(Collectors.toList());

        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("添加学生错误" + ExcelTypeEnum.XLSX.getValue(), "UTF-8"));
        //标题样式
        WriteCellStyle headCellStyle = new WriteCellStyle();
        headCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        //数据样式
        WriteCellStyle contentCellStyle = new WriteCellStyle();
        //设置内容靠左对齐
        contentCellStyle.setHorizontalAlignment(HorizontalAlignment.LEFT);

        HorizontalCellStyleStrategy horizontalCellStyleStrategy =
                new HorizontalCellStyleStrategy(headCellStyle, contentCellStyle);
        ServletOutputStream outputStream = response.getOutputStream();
        try {
            ExcelWriterBuilder write = EasyExcel.write(outputStream, AddUserTemplate.class);
            if (errMsgList != null) {
                write.inMemory(Boolean.TRUE)
                        .registerWriteHandler(new ErrorSheetWriteHandler(errMsgList));
            }
            write.registerWriteHandler(horizontalCellStyleStrategy).sheet("添加学生错误返回").doWrite(errorDtoList);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("错误表格导出异常！");
        } finally {
            outputStream.close();
        }
    }

    /**
     * @param clazz
     * @return 错误数据的坐标和提示
     * @throws
     * @description: 获取注解里ExcelProperty的value，用作校验excel
     */
    public Map<Integer, String> getIndexNameMap(Class clazz,Map<Integer, String> headMap) throws NoSuchFieldException {
        Map<Integer, String> result = new HashMap<>();
        Field field;
        Field[] fields = clazz.getDeclaredFields();
        // 根据表头数量获取注解里ExcelProperty的value的个数
        for (int i = 0; i < headMap.size(); i++) {
            field = clazz.getDeclaredField(fields[i].getName());
            field.setAccessible(true);
            ExcelProperty excelProperty = field.getAnnotation(ExcelProperty.class);
            if (excelProperty != null) {
                int index = excelProperty.index();
                index = index == -1 ? i : index;
                String[] values = excelProperty.value();
                StringBuilder value = new StringBuilder();
                for (String v : values) {
                    value.append(v);
                }
                result.put(index, value.toString());
            }
        }
        return result;
    }
}
